package cn.bytengine.stem.contexts;

import java.util.Map;
import java.util.function.Supplier;

/**
 * <p>
 * Context定义一个执行上下文。上下文内可以包含很多数据信息，这些数据信息支持通过指定类型返回数据内容。
 * </p>
 * <p>
 * 对于有结构化编程需求，建议使用{@link Struct}结构来实现。具体详见{@link Struct}内容。
 * </p>
 * <br />
 * <p>
 * Context的结构可以是嵌套的，可以有一个父级上下文。如果需要一个核心的上下文，可以利用{@link #core()}或者{@link #immutable()}方法来创建。
 * </p>
 * <p>
 * 如果需要初始化数据，也可以通过{@link #core(Map)}或者{@link #core(Map)}方法创建带有初始化数据的上下文。
 * </p>
 * <p>
 * {@link #core()}和{@link #immutable()}创造出来的区别在于，core方法创建的上下文可以后期修改里面的值。而immutable方法创建的上下文，通过初始化数据或者通过builder初始化完成后，上下文里面的数据变为只读。
 * </p>
 * <br />
 * <p>
 * {@link #space(Context)}方法用于创建带有父级上下文的新上下文，该方式用于公共数据的访问。对于分支逻辑中数据的读取，优先读取SpaceContext内的数据。如果当前上下文不存在寻找的数据，SpaceContext会从父级寻找数据。判定是否存在数据，取决于{@link #get(String)}方法返回值值是否为null。
 * </p>
 * <br />
 * <p>
 * 关于代理上下文，主要解决业务操作对象不变的情况，变更执行上下文数据内容。具体详见{@link #proxy(Context)}方法和{@link ProxyContext}类
 * </p>
 */
public interface Context extends Accessor<Context> {
    /**
     * 获取上下文指定key数据
     *
     * @param key 上下文数据key
     * @return 上下文对应key的数据
     */
    Object get(String key);

    /**
     * 获取上下文指定key数据，如果该数据为null，则使用defaultSupplier提供的数据
     *
     * @param key 上下文数据key
     * @param defaultSupplier 默认值提供者
     * @return 上下文对应key的数据
     */
    default Object getOrDefault(String key, Supplier<Object> defaultSupplier) {
        Object val = get(key);
        if (val == null) {
            val = defaultSupplier.get();
        }
        return val;
    }
    /**
     * 获取上下文指定key数据，如果该数据为null，则使用defaultVal作为数据
     *
     * @param key 上下文数据key
     * @param defaultVal 默认值
     * @return 上下文对应key的数据
     */
    default Object getOrDefault(String key, Object defaultVal) {
        return getOrDefault(key, () -> defaultVal);
    }

    /**
     * 通过指定类型返回指定key数据，该方法是{@link #get(String)}的泛型扩展
     *
     * @param key  上下文对应数据的key
     * @param type 上下文数据的类型，转换方式使用了{@link Class#cast(Object)}方法实现
     * @param <T>  指定的类型
     * @return 上下文对应key的数据
     */
    default <T> T getByType(String key, Class<T> type) {
        return type.cast(get(key));
    }

    /**
     * 通过指定类型返回指定key数据，如果该数据为null，则使用defaultSupplier提供的数据，该方法是{@link #getByType(String, Class)}的扩展
     *
     * @param key 上下文对应数据的key
     * @param defaultSupplier 默认值提供者
     * @param type 下文数据的类型，转换方式使用了{@link Class#cast(Object)}方法实现
     * @return 上下文对应key的数据
     * @param <T> 指定的类型
     */
    default <T> T getOrDefaultByType(String key, Supplier<T> defaultSupplier, Class<T> type) {
        T val = getByType(key, type);
        if (val == null) {
            val = defaultSupplier.get();
        }
        return val;
    }
    /**
     * 通过指定类型返回指定key数据，如果该数据为null，则使用defaultSupplier提供的数据，该方法是{@link #getByType(String, Class)}的扩展
     *
     * @param key 上下文对应数据的key
     * @param defaultVal 默认值
     * @param type 下文数据的类型，转换方式使用了{@link Class#cast(Object)}方法实现
     * @return 上下文对应key的数据
     * @param <T> 指定的类型
     */
    default <T> T getOrDefaultByType(String key, T defaultVal, Class<T> type) {
        return getOrDefaultByType(key, (Supplier<T>) () -> defaultVal, type);
    }

    /**
     * 获取字符类型的上下文数据
     *
     * @param key 上下文对应数据的key
     * @return 返回字符串类型的数据
     */
    default String getString(String key) {
        return getByType(key, String.class);
    }

    default String getOrDefaultString(String key, Supplier<String> defaultSupplier) {
        return getOrDefaultByType(key, defaultSupplier, String.class);
    }

    default String getOrDefaultString(String key, String defaultVal) {
        return getOrDefaultString(key, () -> defaultVal);
    }

    /**
     * 获取整形数据类型的上下文数据
     *
     * @param key 上下文对应数据的key
     * @return 返回整形数据类型的数据
     */
    default Integer getInteger(String key) {
        return getByType(key, Integer.class);
    }

    default Integer getOrDefaultInteger(String key, Supplier<Integer> defaultSupplier) {
        return getOrDefaultByType(key, defaultSupplier, Integer.class);
    }

    default Integer getOrDefaultInteger(String key, Integer defaultVal) {
        return getOrDefaultInteger(key, () -> defaultVal);
    }

    /**
     * 获取长整形数据类型的上下文数据
     *
     * @param key 上下文对应数据的key
     * @return 返回长整形数据类型的数据
     */
    default Long getLong(String key) {
        return getByType(key, Long.class);
    }

    default Long getOrDefaultLong(String key, Supplier<Long> defaultSupplier) {
        return getOrDefaultByType(key, defaultSupplier, Long.class);
    }

    default Long getOrDefaultLong(String key, Long defaultVal) {
        return getOrDefaultLong(key, () -> defaultVal);
    }
    /**
     * 获取布尔类型的上下文数据
     *
     * @param key 上下文对应数据的key
     * @return 返回布尔类型的数据
     */
    default Boolean getBoolean(String key) {
        return getByType(key, Boolean.class);
    }

    default Boolean getOrDefaultBoolean(String key, Supplier<Boolean> defaultSupplier) {
        return getOrDefaultByType(key, defaultSupplier, Boolean.class);
    }

    default Boolean getOrDefaultBoolean(String key, Boolean defaultVal) {
        return getOrDefaultBoolean(key, () -> defaultVal);
    }
    /**
     * 设置指定key对应的上下文数据
     *
     * @param key   上下文对应数据的key
     * @param value 上下文对应key的数据
     * @return 返回当前上下文实例，支持链式调用
     */
    Context set(String key, Object value);

    /**
     * 根据指定Map，设置指定key对应的上下文数据
     *
     * @param data 要设置的Map数据内容
     * @return 返回当前上下文实例，支持链式调用
     */
    Context set(Map<String, Object> data);

    /**
     * 判断指定key是否存在上下文中
     *
     * @param key 上下文对应数据的key
     * @return 是否包含指定的key
     */
    boolean has(String key);

    /**
     * 从上下文中删除指定key，如果指定key不存在于上下文，删除操作将不会产生任何影响
     *
     * @param key 上下文对应数据的key
     * @return 返回当前上下文实例，支持链式调用
     */
    Context remove(String key);

    /**
     * 获取当前上下文的父级上下文
     *
     * @return 返回当前上下文的父级上下文，如果不存在，将返回null
     */
    Context parent();

    /**
     * 当前上下文是否包含父级上下文
     *
     * @return 如果有父级上下文，返回true。否则反之。
     */
    boolean hasParent();

    // region helper function

    /**
     * 创建一个空的，可修改的上下文。它不包含父级上下文。
     *
     * @return 空的，可修改的上下文，详见{@link InternalCoreContext}
     */
    static Context core() {
        return new InternalCoreContext();
    }

    /**
     * 创建有初始数据的可修改的上下文。它不包含父级上下文。
     *
     * @param data 初始化数据
     * @return 可修改的上下文，详见{@link InternalCoreContext}
     */
    static Context core(Map<String, Object> data) {
        return new InternalCoreContext(data);
    }

    /**
     * 通过初始数据，创建不可修改的上下文。它不包含父级上下文。
     *
     * @param data 初始化数据
     * @return 不可修改的上下文，详见{@link ImmutableCoreContext}
     */
    static Context immutable(Map<String, Object> data) {
        return ImmutableCoreContext.create(data);
    }

    /**
     * 创建不可修改的上下文构建器。它不包含父级上下文。
     *
     * @return 不可修改的上下文构建器，详见{@link ImmutableCoreContext.Builder}
     */
    static ImmutableCoreContext.Builder immutable() {
        return ImmutableCoreContext.builder();
    }

    /**
     * 根据指定父级上下文，创建新的上下文实例。
     *
     * @param parent 父级上下文
     * @return 上下文实例，详见{@link SpaceContext}
     */
    static Context space(Context parent) {
        return new SpaceContext(parent);
    }

    /**
     * 根据指定父级上下文和初始化数据，创建新的上下文实例。
     *
     * @param parent 父级上下文
     * @param data   初始化数据
     * @return 上下文实例，详见{@link SpaceContext}
     */
    static Context space(Context parent, Map<String, Object> data) {
        return new SpaceContext(parent, data);
    }

    /**
     * 给定被代理上下文，创建代理上下文对象
     *
     * @param proxy 被代理上下文
     * @return 代理上下文
     */
    static ProxyContext proxy(Context proxy) {
        return new ProxyContext(proxy);
    }

    /**
     * 给定被代理上下文，创建不可变上下文代理
     *
     * @param proxy 被代理上下文
     * @return 不可变代理上下文
     */
    static ProxyContext immutableProxy(Context proxy) {
        return new ImmutableProxyContext(proxy);
    }
    // endregion
}
